package openjdk.stream;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

public class CollectorsDemo {
    public static void main(String[] args) {
        joining();
        //listMapCollection();
    }

    private static void joining() {
        String strJoin = Stream.of("1", "2", "3", "4")
                .collect(Collectors.joining(",", "[", "]"));
        System.out.println("strJoin: " + strJoin);
        strJoin = Stream.of("1", "2", "3", "4")
                .collect(Collectors.joining(","));
        System.out.println("strJoin: " + strJoin);
    }

    public void foreachMap(){
        Map<String, Double> m1 =  new HashMap();
        m1.put("a", 0.1);
        m1.put("b", 0.5);
        m1.put("c", 0.6);

        m1.forEach((k,v)->{
            System.out.println("Key : " + k + " Value : " + v);
        });
    }

    /**
     *
     * I have a list of maps as below.
     *
     * And I want to group the entry sets by key, if a key non exist, fill 0 instead.
     *
     *     List<Map<String, Double>> props = Lists.newArrayList();
     *     Map<String, Double> m1 =  Maps.newHashMap();
     *     m1.put("a", 0.1);
     *     m1.put("b", 0.5);
     *     m1.put("c", 0.6);
     *
     *     Map<String, Double> m2 =  Maps.newHashMap();
     *     m2.put("a", 0.3);
     *     m2.put("d", 0.1);
     *
     *     Map<String, Double> m3 = Maps.newHashMap();
     *     m3.put("a", 0.2);
     *
     *     props.add(m1); props.add(m2); props.add(m3);
     * Expected result:
     *
     * {a=[0.1, 0.3, 0.2], b=[0.5, 0, 0], c=[0.6,0,0], d=[0,0.1,0]}
     * I got an idea:
     *
     * find all distinct keys
     * fill every map of missing keys with value 0
     * groupby key, values mapping to a list
     * Any good ideas?
     */
    private static void listMapCollection(){
        List<Map<String, Double>> props = new ArrayList();
        Map<String, Double> m1 =  new HashMap();
        m1.put("a", 0.1);
        m1.put("b", 0.5);
        m1.put("c", 0.6);

        Map<String, Double> m2 =  new HashMap();
        m2.put("a", 0.3);
        m2.put("d", 0.1);

        Map<String, Double> m3 = new HashMap();
        m3.put("a", 0.2);

        props.add(m1);
        props.add(m2);
        props.add(m3);


        Set<String> allKeys = new HashSet();
        props.forEach(prop -> allKeys.addAll(prop.keySet()));
        allKeys.forEach(k -> props.forEach(m -> m.putIfAbsent(k, 0.0)));


        Map<String, List<Double>> collect = props.stream()
                .flatMap(map -> map.entrySet().stream())
                .collect(groupingBy(Map.Entry::getKey,mapping(Map.Entry::getValue, toList())));
        System.out.println(collect);
    }


    private static void listMapCollectionStep2(){
        List<Map<String, Double>> props = new ArrayList();
        Map<String, Double> m1 =  new HashMap();
        m1.put("a", 0.1);
        m1.put("b", 0.5);
        m1.put("c", 0.6);

        Map<String, Double> m2 =  new HashMap();
        m2.put("a", 0.3);
        m2.put("d", 0.1);

        Map<String, Double> m3 = new HashMap();
        m3.put("a", 0.2);

        props.add(m1);
        props.add(m2);
        props.add(m3);


        Map<String, List<Double>> collect = Stream.of(m1, m2, m3)
                .flatMap(map -> map.entrySet().stream())
                .collect(groupingBy(Map.Entry::getKey,mapping(Map.Entry::getValue, toList())));
        System.out.println(collect);


        /**
         *
         * Here static factory methods Collectors.groupingBy() is used to group the concatenated Entries of all maps
         * by the Key (classifier) and mapping method (collector).
         *
         * If its the obvious case you must to fill every map of missing keys with value 0 then there is several way
         * of doing that. One that @abhi stated in other answer. Along with his idea I would like to do that this way
         *
         */


        Set<String> allKeys = new HashSet();
        props.forEach(prop -> allKeys.addAll(prop.keySet()));
        allKeys.forEach(k -> props.forEach(m -> m.putIfAbsent(k, 0.0)));


        /**
         *
         * Infact this will modify your original maps with allKeys having 0 as value ifNotPresent.
         * If this modification causes you problem you can prefer to copy your maps to have separate collections.
         *
         * This will ensure your desired output
         *
         * {a=[0.1, 0.3, 0.2], b=[0.5, 0.0, 0.0], c=[0.6, 0.0, 0.0], d=[0.0, 0.1, 0.0]}
         * Note: You can find this article interesting about Grouping using Java 8 streams
         */
    }





    private static void simple() {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        StringBuilder sb = new StringBuilder();

        for (Integer it : list) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(it);
        }
        System.out.println(sb.toString());
    }



}
